package com.gadarts.parashoot;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import android.graphics.Bitmap;

import com.gadarts.parashoot.enemies.AirCraftEnemy;
import com.gadarts.parashoot.enemies.Infantry;
import com.gadarts.parashoot.enemies.Osprey;
import com.gadarts.parashoot.enemies.Scout;
import com.gadarts.parashoot.enemies.SoldierEnemy;
import com.gadarts.parashoot.misc.Explosion;
import com.gadarts.parashoot.misc.FlyingPart;
import com.gadarts.parashoot.misc.Smoke;
import com.gadarts.parashoot.player.Bunker;
import com.gadarts.parashoot.player.Turret;
import com.gadarts.parashoot.sidekicks.InfantryTower;
import com.gadarts.parashoot.sidekicks.Sidekick;
import com.gadarts.parashoot.utils.C;
import com.gadarts.parashoot.utils.Utils;
import com.gadarts.parashoot.utils.ViewUtils;
import com.gadarts.parashoot.weapons.Bullet;
import com.gadarts.parashoot.weapons.CannonBall;

public class GameModel {

	private boolean init;// Indicates whether the class was initialized.
	private GameView view = null;
	private List<GameButton> buttonsList = new ArrayList<GameButton>();
	private List<Bullet> bulletsList = new ArrayList<Bullet>();// All bullets.
	private List<AirCraftEnemy> airCraftsList = new ArrayList<AirCraftEnemy>();
	private List<SoldierEnemy> soldiersList = new ArrayList<SoldierEnemy>();
	private List<Explosion> explosionsList = new ArrayList<Explosion>();
	private List<Sidekick> sidekickList = new ArrayList<Sidekick>();
	private ArrayList<FlyingPart> flyingPartsList = new ArrayList<FlyingPart>();
	private ArrayList<Smoke> smokeList = new ArrayList<Smoke>();
	private Bunker bunker;// The player's bunker.
	private Turret turret;// The player's turret.
	private GameButton leftButton, rightButton, shootButton;
	private long[] lastTime = new long[20];// Last time checks for timers.
	private Random random = new Random();
	private HashMap<String, Long> timers = new HashMap<String, Long>();
	private int screenHeight, screenWidth;

	// All objects in this list are drawn.
	List<GameObject> drawList = new ArrayList<GameObject>();

	public void update() {// Updates current game's logic.
		if (!init && view != null) {// Initialize class.
			screenWidth = view.getWidth();
			screenHeight = view.getHeight();
			createPlayer();// Creates the bunker and the turret.
			createSidekicks();
			createButtons();// Creates in-game buttons.
			init = true;
		}
		generateEnemy();// Creates an enemy according to current level.
		// Update all active objects in the game.
		for (int i = 0; i < drawList.size(); i++)
			drawList.get(i).update();
	}

	private void createSidekicks() {
		createSidekick();
		createSidekick();
	}

	private void createSidekick() {
		int depth = C.InGame.REGULAR_DEPTH;
		ArrayList<Sprite> sprites = new ArrayList<Sprite>();
		Sprite towerSprite = view
				.getSprite(C.Sidekicks.InfantryTower.IMAGE_NAME);

		sprites.add(towerSprite);

		int x = bunker.getCenterX() - towerSprite.getWidth() * 2;
		int y = getGroundY() - towerSprite.getHeight();
		
		InfantryTower sidekick = new InfantryTower(this, view, sprites, x, y,
				depth);
		
		sidekickList.add(sidekick);
		drawList.add(sidekick);
	}

	private void generateEnemy() {// Creates an enemy according to current
									// level.
		int randomEnemyIndex = random.nextInt(2);
		if (checkTiming(0, 1000/* random.nextInt(15000) */))
			createEnemy(randomEnemyIndex);
	}

	private void addDrawList(GameObject object) {// Add the object to the draw
													// list to tell the view
													// what to draw.
		// Find this object's depth position in the list.
		for (int i = 0; i < drawList.size(); i++)
			if (object.getDepth() < drawList.get(i).getDepth()) {
				drawList.add(i, object);
				return;
			}
		drawList.add(object);
	}

	public void addTimer(String name) {
		timers.put(name, System.currentTimeMillis());
	}

	private void createButtons() {
		leftButton = addButton("leftbutton", 840, 545);
		rightButton = addButton("rightbutton", 950, 545);
		shootButton = addButton("shootbutton", 20, 545);
	}

	private GameButton addButton(String name, int x, int y) {
		int calculatedX = ViewUtils.calculatePosX(x, screenWidth);
		int calculatedY = ViewUtils.calculatePosY(y, screenHeight);
		Sprite images = view.getSprite(name);
		GameButton button = new GameButton(this, view, images, calculatedX,
				calculatedY, 10);
		buttonsList.add(button);
		addDrawList(button);
		return button;
	}

	private void createPlayer() {// Creates the bunker and the turret.
		Sprite bunkerImages = view.getSprite(C.Player.BUNKER_IMAGE_NAME);
		int x = screenWidth / 2 - bunkerImages.get(0).getWidth() / 2;
		int y = ViewUtils.calculatePosY(535, screenHeight)
				- bunkerImages.get(0).getHeight();
		bunker = new Bunker(this, view, bunkerImages, x, y, 1);
		x = bunker.getCenterX();
		y = bunker.getY() + bunker.getHeight() / 5;
		turret = new Turret(this, view, view.getSprite("turret"), x, y, 270, 0);
		addDrawList(bunker);
		addDrawList(turret);
	}

	public void createBullet(Bitmap image, int x, int y, int speed,
			int direction, boolean myBullet, int depth) {
		Bullet newBullet = new CannonBall(this, view, image, x, y, speed,
				direction, myBullet, depth);
		bulletsList.add(newBullet);
		addDrawList(newBullet);
	}

	public void createBullet(Sprite sprite, int x, int y, int speed,
			int direction, boolean myBullet, int depth) {
		Bullet newBullet = new CannonBall(this, view, sprite.get(0), x, y,
				speed, direction, myBullet, depth);
		bulletsList.add(newBullet);
		addDrawList(newBullet);
	}

	private void createEnemy(int enemy) {// Create an enemy in the right or left
											// side of the view.
		AirCraftEnemy newEnemy = null;
		int position = random.nextInt(2), newX, newDirection;
		if (position == 0) {// Left or right side of the screen.
			newX = 0;
			newDirection = 0;
		} else {
			newX = screenWidth;
			newDirection = 180;
		}
		Sprite sprite;
		Bitmap partImage;
		int width;
		int speed;
		switch (enemy) {
		case 0:// Scout.
			sprite = view.getSprite(C.Enemies.AirCrafts.Scout.IMAGE_NAME);
			partImage = view
					.getImage(C.Enemies.AirCrafts.Scout.SCOUT_PART_IMAGE_NAME);
			width = view.getImage(C.Enemies.AirCrafts.Scout.IMAGE_NAME)
					.getWidth();
			speed = ViewUtils.calculatePosX(C.Enemies.AirCrafts.Scout.SPEED,
					screenWidth);
			newEnemy = new Scout(this, view, sprite, newX - width, 50, speed,
					newDirection, C.Enemies.ENEMY_DEPTH,
					C.Enemies.AirCrafts.Scout.HEALTH, true, partImage);
			airCraftsList.add(newEnemy);
			break;
		case 1:// Osprey.
			sprite = view.getSprite(C.Enemies.AirCrafts.Osprey.IMAGE_NAME);
			partImage = view
					.getImage(C.Enemies.AirCrafts.Scout.SCOUT_PART_IMAGE_NAME);
			width = view.getImage(C.Enemies.AirCrafts.Osprey.IMAGE_NAME)
					.getWidth();
			speed = ViewUtils.calculatePosX(C.Enemies.AirCrafts.Osprey.SPEED,
					screenWidth);
			newEnemy = new Osprey(this, view, sprite, newX - width, 50, speed,
					newDirection, C.Enemies.ENEMY_DEPTH,
					C.Enemies.AirCrafts.Osprey.HEALTH, true, partImage);
			airCraftsList.add(newEnemy);
			break;
		}
		addDrawList(newEnemy);
	}

	public void createExplosion(int x, int y, int size) {
		String resId;
		if (size == 0)
			resId = C.InGame.Misc.Explosion.SMALL_EXP_IMAGE_NAME;
		else
			resId = C.InGame.Misc.Explosion.BIG_EXP_IMAGE_NAME;
		Sprite expImages = view.getSprite(resId);
		int width = expImages.get(0).getWidth();
		int height = expImages.get(0).getHeight();
		x = x - width / 2;
		y = y - height / 2;
		int depth = C.InGame.REGULAR_DEPTH;
		Explosion exp = new Explosion(this, view, expImages, x, y, depth);
		explosionsList.add(exp);
		addDrawList(exp);
	}

	public void createFlyingPart(int x, int y, Bitmap image, boolean towardsTop) {
		x = x - image.getWidth() / 2;
		y = y - image.getHeight() / 2;
		int speed = ViewUtils.calculatePosX(C.InGame.Misc.FLYING_PART_SPEED,
				screenWidth);
		int direction = Utils.getRandomDirection(towardsTop);

		FlyingPart part = new FlyingPart(this, view, image, x, y, speed,
				direction, C.InGame.REGULAR_DEPTH);
		flyingPartsList.add(part);
		addDrawList(part);
	}

	public void createFlyingPart(int x, int y, Bitmap image) {
		createFlyingPart(x, y, image, false);
	}

	public void createSmoke(int x, int y, int size) {
		String resId;
		if (size == 0)
			resId = C.InGame.Misc.Smoke.SMALL_SMOKE_IMAGE_NAME;
		else
			resId = C.InGame.Misc.Smoke.BIG_SMOKE_IMAGE_NAME;
		Sprite images = view.getSprite(resId);
		if (images != null) {
			x = x - view.getImage(resId).getWidth() / 2;
			y = y - view.getImage(resId).getHeight() / 2;
			Smoke smoke = new Smoke(this, view, images, x, y,
					C.InGame.REGULAR_DEPTH);
			smokeList.add(smoke);
			addDrawList(smoke);
		} else {
			Utils.logWarning(resId + " WAS NOT FOUND!");
		}
	}

	public void createParatrooper(int x, int y) {
		int speed = ViewUtils.calculatePosY(
				C.Enemies.Paratroopers.Infantry.SPEED, view.getHeight());

		ArrayList<Sprite> sprites = new ArrayList<Sprite>();
		Sprite sinkSprite = view
				.getSprite(C.Enemies.Paratroopers.Infantry.SINKING_IMAGE_NAME);
		Sprite fallSprite = view
				.getSprite(C.Enemies.Paratroopers.Infantry.FALLING_IMAGE_NAME);
		Sprite shootLeftSprite = view
				.getSpriteWithReverse(C.Enemies.Paratroopers.Infantry.SHOOTING_LEFT_IMAGE_NAME);
		Sprite shootRightSprite = view
				.getSpriteWithReverse(C.Enemies.Paratroopers.Infantry.SHOOTING_RIGHT_IMAGE_NAME);
		shootRightSprite.addReverse();
		Sprite dieSprite = view
				.getSprite(C.Enemies.Paratroopers.Infantry.DIE_IMAGE_NAME);
		sprites.add(sinkSprite);
		sprites.add(fallSprite);
		sprites.add(shootLeftSprite);
		sprites.add(shootRightSprite);
		sprites.add(dieSprite);

		Bitmap bodyPart = view
				.getImage(C.Enemies.Paratroopers.Infantry.BODY_PART_IMAGE_NAME);

		int health = C.Enemies.Paratroopers.Infantry.HEALTH;
		int depth = C.InGame.REGULAR_DEPTH;

		Infantry paratrooper = new Infantry(this, view, sprites, x, y, speed,
				90, depth, health, false, bodyPart);

		soldiersList.add(paratrooper);
		addDrawList(paratrooper);
	}

	public void setView(GameView view) {
		this.view = view;
	}

	public List<Bullet> getBulletsList() {
		return bulletsList;
	}

	// Get the DP of the ground height.
	public int getGroundY() {
		return ViewUtils.calculatePosY(C.InGame.GROUND_Y, screenHeight);
	}

	public HashMap<String, Long> getTimers() {
		return timers;
	}

	// When finger presses screen.
	protected void actionDownEvent(float x, float y) {
		GameButton currentButton;

		// Check if there are any buttons in this position.
		for (int i = 0; i <= buttonsList.size() - 1; i++) {
			currentButton = buttonsList.get(i);
			String leftButtonImage = C.InGame.Hud.Buttons.LEFT_BUTTON_IMAGE;
			String rightButtonImage = C.InGame.Hud.Buttons.RIGHT_BUTTON_IMAGE;
			String shootButtonImage = C.InGame.Hud.Buttons.SHOOT_BUTTON_IMAGE;
			if (ViewUtils.checkCollision(currentButton, (int) x, (int) y))
				if (currentButton.getImage() == view.getImage(leftButtonImage))
					turret.setRotation(-C.Player.ROTATE_SPEED);
				else if (currentButton.getImage() == view
						.getImage(rightButtonImage))
					turret.setRotation(C.Player.ROTATE_SPEED);
				else if (currentButton.getImage() == view
						.getImage(shootButtonImage))
					turret.setShootingState(true);
		}
	}

	protected void actionMoveEvent(float x, float y) {// When finger moves on
														// screen.
		if (x >= screenWidth / 2) {// Only if it's in the right side of the
									// screen.
			if (ViewUtils.checkCollision(rightButton, (int) x, (int) y) == false
					&& ViewUtils.checkCollision(leftButton, (int) x, (int) y) == false)// Checks
																						// whether
																						// the
																						// buttons
																						// touch
																						// this
																						// position.
				turret.setRotation(0);
		} else if (x < screenWidth / 2)// Only if it's in the left side of
										// the screen.
			if (ViewUtils.checkCollision(shootButton, (int) x, (int) y) == false)// Checks
																					// whether
																					// the
																					// shooting
																					// button
																					// touches
																					// this
																					// position.
				turret.setShootingState(false);
	}

	// When finger releases push.
	protected void actionReleaseEvent(float x, float y) {
		if (ViewUtils.checkCollision(rightButton, (int) x, (int) y)
				|| ViewUtils.checkCollision(leftButton, (int) x, (int) y))
			turret.setRotation(0);
		if (ViewUtils.checkCollision(shootButton, (int) x, (int) y))
			turret.setShootingState(false);
	}

	// Set a timer.
	protected boolean checkTiming(int timerNum, int diff) {
		if (lastTime[timerNum] > 0)
			if (System.currentTimeMillis() - lastTime[timerNum] >= diff) {
				lastTime[timerNum] = System.currentTimeMillis();
				return true;
			} else
				return false;
		else {
			lastTime[timerNum] = System.currentTimeMillis();
			return false;
		}
	}

	public void destroyObject(GameObject gameObject,
			Class<? extends Object> objectClass) {
		// Remove the object from the drawing list.
		for (int i = 0; i < drawList.size(); i++)
			if (drawList.get(i) == gameObject)
				drawList.remove(gameObject);
		// Now delete it's existence.
		if (gameObject == bunker)// Bunker.
			bunker = null;
		else if (gameObject == turret)// Turret.
			turret = null;
		else if (objectClass == GameButton.class)// Game button.
			buttonsList.remove(gameObject);
		else if (objectClass == Bullet.class)// Bullet.
			bulletsList.remove(gameObject);
		else if (objectClass == AirCraftEnemy.class)// AirCraft Enemy.
			airCraftsList.remove(gameObject);
		else if (objectClass == SoldierEnemy.class)// Soldier.
			soldiersList.remove(gameObject);
		else if (objectClass == Explosion.class)// Explosion.
			explosionsList.remove(gameObject);
		else if (objectClass == Smoke.class)// Smoke.
			smokeList.remove(gameObject);
	}

	public List<SoldierEnemy> getSoldiers() {
		return soldiersList;
	}

	public Bunker getBunker() {
		return bunker;
	}

	public List<AirCraftEnemy> getAirCraftsList() {
		return airCraftsList;
	}

}
